#include <iostream>
#include <cstring>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>

#include"Logger.hpp"

using namespace std;
using namespace LogMudule;




class UdpServer
{
    enum UdpServer_error
    {
        SOCKET_ERROR=1,//socket创建失败
        BIND_ERROR//bind失败
    };
    string ErrortoString(UdpServer_error e)
    {
        string s;
        switch(e)
        {
            case SOCKET_ERROR:
            s="deadly: socket_error";
            break;
            case BIND_ERROR:
            s="deadly: bind_error";
            break;
            default:
            s="deadly unknown error";//未知错误
            break;
        }
        return s;
    }

    static const uint16_t default_port=8080;//默认端口
    static const uint32_t default_bufsize=1024;//接收缓冲区默认大小
    static const int default_sockfd=-1;

    typedef function<bool(const string&,string&)>  DictHandle_t;
public:
    UdpServer(uint16_t port=default_port)
        :_port(port),
        _socketfd(default_sockfd),
        _isrunning(false)
    {
        int n=socket(AF_INET,SOCK_DGRAM,0);//创建socket
        if(n>0)//大于0，表示创建成功
        {
            _socketfd=n;//返回文件描述符
            LOG(LogLevel::DEBUG)<<"创建udp_socket成功";
        }
        else
        {
            LOG(LogLevel::DEADLY)<<ErrortoString(SOCKET_ERROR);
            exit(SOCKET_ERROR);//创建socket失败,服务器就无法接受网络消息，无法运行，直接退出
        }
        struct sockaddr_in addr;//网络socket

        addr.sin_family=AF_INET;//网络协议族

        addr.sin_addr.s_addr=htonl(INADDR_ANY);//服务器进程只是 主机固定 ，但是IP并不一定固定
        //所以只要是发给当前主机的xinx，端口号是成员变量_port的信息，那我们的服务器照单全收

        addr.sin_port=htons(_port);//注意要发送到网络的ip和端口号，需要转化成网络序列（即大端）

        int i=bind(_socketfd,(sockaddr*)&addr,sizeof(addr));
        if(i<0)
        {
            LOG(LogLevel::DEADLY)<<ErrortoString(BIND_ERROR);
            exit(BIND_ERROR);//bind失败,服务器就无法接受网络消息，无法运行，直接退出
        }
        LOG(LogLevel::DEBUG)<<"bind成功";
    }
    void Registration(DictHandle_t dict)//用于上层注册业务函数
    {
        _dict_handle=dict;
    }
    void Start()
    {
        LOG(LogLevel::DEBUG)<<"服务器开始运行...";
        _isrunning=true;
        while(_isrunning)//只要_isrunning为true，服务器就不停
        {
            char rbuf[default_bufsize]={0};//接收缓冲区

            struct sockaddr_in clientaddr;//用于客户端存储客户端的ip和端口
            socklen_t addr_len=sizeof(clientaddr);

            //读取客户端发来的数据，以及获取客户端的ip和端口号
            int n=recvfrom(_socketfd,rbuf,sizeof(rbuf),0,(sockaddr*)&clientaddr,&addr_len);
            if(n>0)//读取成功
            {
                rbuf[n]='\0';//添加字符串尾

                char ip_buf[100]={0};

                //把网络序列的4字节IP,转成主机序列的点分十进制的IP
                inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ip_buf,sizeof(ip_buf));

                LOG(LogLevel::DEBUG)<<"成功接收来自IP:"<<ip_buf<< " ,port: "<<ntohs(clientaddr.sin_port)
                <<"的消息: "<<rbuf;

                //根据客户端发来的消息进行翻译
                string out;
                if(!_dict_handle(rbuf,out))//回调上层注册进来的业务函数，进行翻译
                {
                    LOG(LogLevel::ERROR)<<"从客户端获取的单词: "<<rbuf<<"，在字典中不存在";
                }
                string wbuf="server echo# ";
                wbuf+=out;
                //给客户端回消息
                sendto(_socketfd,wbuf.c_str(),wbuf.size(),0,(sockaddr*)&clientaddr,sizeof(clientaddr));
            }
            else//recvfrom调用失败，读取错误
            {
                LOG(LogLevel::ERROR)<<"error: recvfrom error";
            }
        }
    }
    void Stop()
    {
        _isrunning=false;
        LOG(LogLevel::DEBUG)<<"服务器停止运行";
    }

    ~UdpServer()
    {
        close(_socketfd);
    }
private:
    int _socketfd;
    uint16_t _port;//端口号
    bool _isrunning;//是否运行
    DictHandle_t _dict_handle;//字典类注册进来的回调函数
};